#include <iostream>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>

using namespace std;

#define BIT_1	1
#define BIT_2	2
#define BIT_3	4
#define BIT_4	8
#define BIT_5	16
#define BIT_6	32
#define BIT_7	64
#define BIT_8	128
#define BIT_9	256
#define BIT_10	512

#define BITMASK_RIGHT	(BIT_1 | BIT_2 | BIT_3 | BIT_4)

#define DBG(desc, num, bits)

static short int sbox0[][4] = {
								1, 0, 3, 2,
								3, 2, 1, 0,
								0, 2, 1, 3,
								3, 1, 3, 2
							  };
static short int sbox1[][4] = {
								0, 1, 2, 3,
								2, 0, 1, 3,
								3, 0, 1, 0,
								2, 1, 0, 3
							  };


/**
 * F ham con trong ham Fk. Dau vao su dung 4-bits de thuc thi voi key con.
 * anh xa 4 bit vao  e/p, xor with subkey va map sbox + hoan vi
 *
 * dau vao input 4-bits
 * sk subkey de thuc thi
 */
static char f(char input, char sk) 
{
	short int row0=0,col0=0,row1=0,col1=0;
	char out='\0',ep='\0',aux='\0';

	DBG("f() input: ", (int)input, 8);

	// E/P
	aux = !!(input & BIT_1);
	ep |= ((aux << 7) | (aux << 1));
	aux = !!(input & BIT_2);
	ep |= ((aux << 4) | (aux << 2));
	aux = !!(input & BIT_3);
	ep |= ((aux << 5) | (aux << 3));
	aux = !!(input & BIT_4);
	ep |= ((aux << 6) | aux);
	DBG("E/P: ", (int)ep, 8);

	// xor voi subkey
	ep = ep ^ sk;
	DBG("E/P ^ sk: ", (int)ep, 8);

	// tinh toan hang va cot tu sboxes
	row0 = !!(ep & BIT_8);
	row0 = (row0 << 1) | !!(ep & BIT_5);
	col0 = !!(ep & BIT_7);
	col0 = (col0 << 1) | !!(ep & BIT_6);
	row1 = !!(ep & BIT_4);
	row1 = (row1 << 1) | !!(ep & BIT_1);
	col1 = !!(ep & BIT_3);
	col1 = (col1 << 1) | !!(ep & BIT_2);

	// P4 (2,4,3,1)
	out = !!(sbox0[row0][col0] & BIT_1);
	out = (out << 1) | !!(sbox1[row1][col1] & BIT_1);
	out = (out << 1) | !!(sbox1[row1][col1] & BIT_2);
	out = (out << 1) | !!(sbox0[row0][col0] & BIT_2);
	DBG("f() out: ", (int)out, 8);

	return(out);
}

/**
 * Ham Fk
 *
 * input dau vao cua ham Fk
 * sk1  subkey dau tien duoc su dung
 * sk2	subkey thu 2 duoc su dung
 */
static char fk(char input, char sk1, char sk2) 
{
	char l='\0',r='\0',out='\0';

	DBG("Fk IN: ", (int)input, 8);
	// first 4 bits
	r = (input & BITMASK_RIGHT);
	l = (input >> 4);
	out = ((f(r, sk1) ^ l) & BITMASK_RIGHT) | (r << 4);
	DBG("Fk OUT + Swap: ", (int)out, 8);
	// second 4 bits
	r = (out & BITMASK_RIGHT);
	l = (out >> 4);
	out = ((f(r, sk2) ^ l) << 4) | r;
	DBG("Fk OUT: ", (int)out, 8);
	return(out);
}

/**
 * tao subkey 8-bit tu key 10-bit
 *
 * key  10-bit key
 * sk1  subkey dau tien duoc tao ra
 * sk2  subkey thu hai duoc tao ra
 */
static void generate_sub_keys(short int key, char *sk1, char *sk2) 
{
	short int i;
	short int k1_order[] = { BIT_10, BIT_4, BIT_2, BIT_7, BIT_3, BIT_8, BIT_1, BIT_5 };
	short int k2_order[] = { BIT_3, BIT_8, BIT_5, BIT_6, BIT_1, BIT_9, BIT_2, BIT_10 };

	*sk1 = '\0';
	*sk2 = '\0';
	for (i = 0; i < 8; i++) 
	{
		*sk1 = (*sk1 << 1) | !!(k1_order[i] & key);
		*sk2 = (*sk2 << 1) | !!(k2_order[i] & key);
	}
}

//ham ip
static char ip(char byte) 
{
	short int i;
	char ret;
	short int order[] = { BIT_7, BIT_3, BIT_6, BIT_8, BIT_5, BIT_1, BIT_4, BIT_2 };

	ret = '\0';
	//xu ly bit
	for (i = 0; i < 8; i++) 
	{
		ret = (ret << 1) | !!(order[i] & byte);
	}
	return(ret);
}

//ham ip^-1
static char ip_inverse(char byte) 
{
	short int i;
	char ret;
	short int order[] = { BIT_5, BIT_8, BIT_6, BIT_4, BIT_2, BIT_7, BIT_1, BIT_3 };

	ret = '\0';
	for (i = 0; i < 8; i++) 
	{
		ret = (ret << 1) | !! (order[i] & byte);
	}
	return(ret);
}

long filesize(FILE *fp)
{
    fseek(fp, 0, SEEK_END); 
    return ftell(fp);
}

int main(int argc, const char* argv[]) 
{
	FILE		*fp,*in,*out;
	short int	key;
	char		ch,sk1,sk2;
	int			dem = 0;

	cout << "input : ";
	cin >> key;
	
	if ((key < 0 || key > 1023)) 
	{	
		cout << "Error key :" << endl;
		return(1);
	}
	key = (key % 1024);

	generate_sub_keys(key,&sk1,&sk2);

	fp = fopen("plaintext.txt", "rb");
	in = fopen("plaintext.txt", "rb");
	if (!in) 
	{
		cout << "file not found\n";
		return(1);
	}

	out = fopen("ciphertext.txt", "wb+");
	if (!out) 
	{
		cout << "Error file \n";
		fclose(in);
		return(1);
	}


	while (!feof(in) && dem < filesize(fp)) {
		ch = fgetc(in);
	
		ch = ip(ch);
		
		ch = fk(ch, sk1, sk2);
	
		ch = ip_inverse(ch);
		fputc((char)ch, out);
		dem = dem + 1;
	}
	fclose(in);
	fclose(out);
	fclose(fp);

	return 0;
}

